
#ifndef _NEBULA_SDK__MANAGEDSERVICE_H_
#define _NEBULA_SDK__MANAGEDSERVICE_H_

#include "ServiceBase.h"

#include <map>
#include <string>
#include <vector>

#include "FuncAgent.h"
#include "TaskProcessor.h"

#include "Utility/RefPtr.h"
#include "Utility/ErrorCode.h"

namespace nebula { namespace sdk {

//redefine this macro before include the header file to support more function binders
#ifndef MAX_FUNCAGENT
#define MAX_FUNCAGENT 16 
#endif

template<typename ServiceType>
class ManagedService : public ServiceBase
{
public:
	//Event handlers:
	//The following 4 functions must be implemented by client
	virtual NEBULA_ERROR_CODE OnInitialize () = 0;	
	virtual void              OnFinalize   () = 0;
	virtual NEBULA_ERROR_CODE OnStart      () = 0;
	virtual void              OnStop       () = 0;

public:
	//The following 4 functions must be called in their correspondent event handler
	NEBULA_ERROR_CODE Initialize ();
	void              Finalize   ();
	NEBULA_ERROR_CODE Start      ();
	void              Stop       ();

private:
	typedef ServiceType service_type;	

	typedef ITask*   (*static_task_alloc)(IService*);
	typedef ITask*   (service_type::*member_task_alloc) ();

	typedef NEBULA_ERROR_CODE (service_type::*push_handler)  (std::auto_ptr<ITask>);
	typedef NEBULA_ERROR_CODE (service_type::*query_handler) (IRequest*, ITask**);
	typedef NEBULA_ERROR_CODE (service_type::*delete_handler)(IRequest*);
	typedef NEBULA_ERROR_CODE (service_type::*update_handler)(IRequest*, ITask*, ITask**);


public:
	//built-in Thread Pool management
	NEBULA_ERROR_CODE StartThreadPool();
	void              StopThreadPool();

	//register helpers to reduce the efforts of registering slots to frameworks
	NEBULA_ERROR_CODE RegisterPushSlot(
		const char* name, member_task_alloc alloc, push_handler handler, 
		unsigned min_thread = 1, unsigned max_thread = 1
		);

	NEBULA_ERROR_CODE RegisterPushSlot(
		const char* name, static_task_alloc alloc, push_handler handler, 
		unsigned min_thread = 1, unsigned max_thread = 1
		);

	NEBULA_ERROR_CODE RegisterPopSlot(
		const char* name, IDoneTaskQueue** queue
		);

	NEBULA_ERROR_CODE RegisterQuerySlot (const char* name, query_handler  handler);
	NEBULA_ERROR_CODE RegisterDeleteSlot(const char* name, delete_handler handler);

	NEBULA_ERROR_CODE RegisterUpdateSlot(const char* name, static_task_alloc alloc, update_handler handler);
	NEBULA_ERROR_CODE RegisterUpdateSlot(const char* name, member_task_alloc alloc, update_handler handler);

private:
	//////////////////////////////////////////////////////////////////////////	
	//push slot
	struct PushSlotDesc
	{
		PushSlotDesc(const char* name, push_handler proc, int min_, int max_) : 
			min_thread(min_),
			max_thread(max_),
			processor(name), 
			queue(0), 
			handler(proc)
		{
		}

		unsigned Start(service_type* p)
		{
			return processor.start(p, handler, queue, min_thread, max_thread);
		}

		void Stop()
		{
			processor.stop();
		}

		int min_thread;
		int max_thread;
		push_handler     handler;
		ITodoTaskQueue*  queue;
		TaskProcessor<service_type> processor;
	};

	typedef utility::ref_ptr<PushSlotDesc> PushSlotDescRptr;

	typedef std::map<std::string, PushSlotDescRptr> PushSlotMap;

	PushSlotMap _push_slot_table;

	//////////////////////////////////////////////////////////////////////////	
	//pop slot

	//////////////////////////////////////////////////////////////////////////	
	//query slot


private:
	template<int N>
	struct AllocFuncTrait
	{
		typedef member_task_alloc host_func_t;
		typedef ITask* (*agent_func_t)(IService*);

		static host_func_t host(host_func_t val)
		{
			static host_func_t _host = 0;
			if (_host)
				return _host;
			_host = val;
			return _host;
		}

		static ITask* agent(IService* pservice)
		{
			service_type* p = dynamic_cast<service_type*>(pservice);
			host_func_t pfunc = AllocFuncTrait<N>::host(0);
			if (p && pfunc)
				return (p->*pfunc)();
			else
				return NEBULA_ERR_SUCCESS;
		}
	};

	template<int N>
	struct QueryFuncTrait
	{

		typedef query_handler host_func_t;
		typedef unsigned (*agent_func_t)(IService*, IRequest*, ITask**);

		static host_func_t host(host_func_t val)
		{
			static host_func_t _host = 0;
			if (_host)
				return _host;
			_host = val;
			return _host;
		}

		static unsigned agent(IService* service, IRequest* request, ITask** task)
		{
			service_type* p = dynamic_cast<service_type*>(service);
			host_func_t pfunc = QueryFuncTrait<N>::host(0);
			if (p && pfunc)
				return (p->*pfunc)(request, task);
			else
				return NEBULA_ERR_INTERNAL_ERROR;
		}
	};
	
	template<int N>
	struct DeleteFuncTrait
	{

		typedef delete_handler host_func_t;
		typedef unsigned (*agent_func_t)(IService*, IRequest*);

		static host_func_t host(host_func_t val)
		{
			static host_func_t _host = 0;
			if (_host)
				return _host;
			_host = val;
			return _host;
		}

		static unsigned agent(IService* service, IRequest* request)
		{
			service_type* p = dynamic_cast<service_type*>(service);
			host_func_t pfunc = DeleteFuncTrait<N>::host(0);
			if (p && pfunc)
				return (p->*pfunc)(request);
			else
				return NEBULA_ERR_INTERNAL_ERROR;
		}
	};

	template<int N>
	struct UpdateFuncTrait
	{
		typedef update_handler host_func_t;
		typedef unsigned (*agent_func_t)(IService*, IRequest*, ITask*, ITask**);

		static host_func_t host(host_func_t val)
		{
			static host_func_t _host = 0;
			if (_host)
				return _host;
			_host = val;
			return _host;
		}

		static unsigned agent(IService* service, IRequest* request, ITask* intask, ITask** outtask)
		{
			service_type* p = dynamic_cast<service_type*>(service);
			host_func_t pfunc = UpdateFuncTrait<N>::host(0);
			if (p && pfunc)
				return (p->*pfunc)(request, intask, outtask);
			else
				return NEBULA_ERR_INTERNAL_ERROR;
		}
	};

private:
	FuncAgentWrapper<MAX_FUNCAGENT, AllocFuncTrait>  _allocator;
	FuncAgentWrapper<MAX_FUNCAGENT, UpdateFuncTrait> _updater;
	FuncAgentWrapper<MAX_FUNCAGENT, DeleteFuncTrait> _deleter;
	FuncAgentWrapper<MAX_FUNCAGENT, QueryFuncTrait>  _querier;

};

}} // end of nebula::sdk

#include "ManagedServiceImpl.h"

#endif // _NEBULA_SDK__MANAGEDSERVICE_H_


